import * as htmlparser2 from 'htmlparser2';

export interface MdcTable {
  tableType: string,
  title: string,
  content: string,
  desc?: string,
}

export function isMdcTable(mdc: any): mdc is MdcTable {
  // @ts-ignore
  return !!mdc.tableType
}

interface XmlNode {
  type: 'tag' | 'text',
  name: string,
  data?: string,
  children?: XmlNode[],
  attribs: { [k: string]: string },
}

export function processTableString(tableString: string) {
  try {
    //https://astexplorer.net/#/2AmVrGuGVJ
    const parseXml = htmlparser2.parseDocument;
    const jsonData = parseXml(tableString,{xmlMode:true}) as any as XmlNode;
    // console.log('jsonData:',jsonData);
    const root = jsonData.children![0];
    let tableDesc: string | undefined = undefined;

    // title 表格标题
    const {attribs: {title}, name: tableType} = root;
    // console.log('title:', title);
    // console.log('tableType:', tableType);

    const propsList = root.children!.reduce((ret: { [k: string]: string }[], child: XmlNode) => {
      // 忽略文本节点
      if (child.type === "tag") {
        if (child.name === 'desc') {
          if (!!child.children && child.children.length > 0) {
            tableDesc = child.children[0].data
          }
        } else {
          // xml节点里只有它的属性是我们所需要的
          //<item name="data" type="array:any[]" desc="显示的数据"/>
          //↓
          //{desc: "显示的数据", name: "data", type: "array:any[]"}
          const item = {...(child.attribs || {})}
          // console.log('pre item:',item)
          child.children!.forEach(subChild => {
            if (subChild.type === "tag") {
              // console.log('subChild:',subChild) //比如在一个表格的单元格内再套一个ul列表
              /*
                  <item name="validate">
                    <params>
                      ...
                    </params>
                  </item>
                  params就会作为item的一个属性
              */
              //转换成的ast长这样↓
              //{type: "element", name: "params", attributes: {…}, children: Array(3), parent: {…}, …}

              item[subChild.name] = toXmlString(subChild, true).replace(/\n/g, '')
              // console.log('item[subChild.name]:',item[subChild.name]); //item[subChild.name]: <ul><li><code>callback</code>？：回调函数，不传则通过调用返回的promise.then处理回调</li><li><code>loadingMask</code>？：是否在校验的时候显示表单加载遮罩，默认显示遮罩</li></ul>
            }
          })

          // 如果该属性是一个复杂的类型, 我们需要把它变成link, 点击能跳转到对应的锚点显示该类型的详细信息
          if (!!item.type && item.type.indexOf('TYPE ') === 0) {
            // console.log('item:',item);
            item.type = `<a href="${item.type}">${item.type}</a>`
            // console.log('item.type:',item.type);
          }
          // console.log('post item:',item)
          ret.push(item)
        }
      }
      // console.log('ret:',ret)
      return ret
    }, [])

    return {
      tableType,
      desc: tableDesc,
      title,
      content: buildSpecificMarkdownTableByPropsAndType(tableType, propsList),
    } as MdcTable

  } catch (e) {
    console.error('解析tableString失败！');
    console.error('解析的内容为:', tableString);
    throw e
  }
}

export const MATCH_TABLE_STRING = /```xml2table((?:.|\r|\n|\t)*?)```/g

//obj ---> {type: "tag", name: "params", attributes: {…}, children: Array(3), parent: {…}, …}
// ↓ 转换为
//<ul><li><code>callback</code>？：回调函数，不传则通过调用返回的promise.then处理回调</li><li><code>loadingMask</code>？：是否在校验的时候显示表单加载遮罩，默认显示遮罩</li></ul>
function toXmlString(obj: XmlNode, removeRootTag = false): string {
  //虽然我们在外面调用 toXmlString 时传的一定是个.type为element的
  //但这个传进来的节点可能还有子节点, 在我们递归调用时, 可能会存在节点类型为text的情况
  if (obj.type === 'text') {
    // console.log('obj:', obj);
    return obj.data!.trim() == '' ? '' : obj.data as string
  }

  //removeRootTag为true的话
  //比如 <params><ul>...</ul></params>
  //就会去掉 <params></params>, 只保留内部的 <ul></ul>
  return `${!removeRootTag ? `<${obj.name}>` : ''}${!!obj.children && obj.children.length > 0 ? obj.children.map((child: any) => toXmlString(child)).join('') : obj.data}${!removeRootTag ? `</${obj.name}>` : ''}`
}

/*已经定义的table类型*/
const types = [] as TableType[]
interface TableType {
  name: string,               // 表格的类型
  attrs: TableAttr[],         // 这种类型下表格所拥有的属性
}
interface TableAttr {
  name: string,
  title: string,
}
function buildSpecificMarkdownTableByPropsAndType(tableType: string, propsList: { [k: string]: string }[]): string {
  for (let i = 0; i < types.length; ++i) {
    const type = types[i];
    if (type.name === tableType) {
      const {attrs} = type;

      return `|${attrs.map(attr => attr.title).join('|')}|`
        + `\n|${attrs.map(() => ':--').join('|')}|`
        + `\n` + propsList.map(props => {
          return `|${attrs.map(attr => props[attr.name] || '').join('|')}|`
        }).join('\n');
    }
  }

  console.error('无法识别tableType:' + tableType);

  return '';
}

export function registryTableType(name: string, attrs: TableAttr[]) {
  types.push({
    name,
    attrs,
  })
}

registryTableType('props', [
  {name: 'name', title: '属性名'},
  {name: 'type', title: '类型'},
  {name: 'default', title: '默认值'},
  {name: 'desc', title: '说明'},
]);

registryTableType('slots', [
  {name: 'name', title: '插槽名称'},
  {name: 'desc', title: '说明'},
]);

registryTableType('methods', [
  {name: 'name', title: '方法名'},
  {name: 'params', title: '参数说明'},
  {name: 'desc', title: '说明'},
]);

registryTableType('scoped-slots', [
  {name: 'name', title: '作用域插槽名'},
  {name: 'params', title: '参数说明'},
  {name: 'desc', title: '说明'},
]);

registryTableType('events', [
  {name: 'name', title: '事件名'},
  {name: 'params', title: '参数'},
  {name: 'desc', title: '说明'},
]);
